package com.learn.datetime;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 封装计时工具StopWatch
 *
 * @author wsh
 * @date 2022-03-27 22:56
 */
public class StopWatchUtil {
    private static final Logger log = LoggerFactory.getLogger(StopWatchUtil.class);
    private static final ThreadLocal<String> localId = new ThreadLocal<String>();
    private static final ThreadLocal<Map<String, Map<String, StopWatch>>> localWatchs = new ThreadLocal<>();

    /**
     * 停止并开始一个新的计时
     *
     * @param className  类名
     * @param methodName 方法名
     * @param signName   标记
     * @date 2022/3/27
     */
    public static void stopAndStartNew(String className, String methodName, String signName) {
        stop(className, methodName);
        start(className, methodName, signName);
    }

    /**
     * 开始计时
     *
     * @param className  类名
     * @param methodName 方法名
     * @param signName   标记
     * @date 2022/3/29
     */
    public static void start(String className, String methodName, String signName) {
        /*signName = StrUtil.builder()
                .append(getId())
                .append(":")
                .append(signName).toString();*/
        // TODO 存在误差，signName可能重复
        signName = StrUtil.builder()
                .append(className)
                .append(".")
                .append(methodName)
                .append("():")
                .append(signName).toString();
        getByClassAndMethod(className, methodName).start(signName);
    }

    /**
     * 停止计时
     *
     * @param className  类名
     * @param methodName 方法名
     * @date 2022/3/29
     */
    public static void stop(String className, String methodName) {
        StopWatch stopWatch;
        if ((stopWatch = getByClassAndMethod(className, methodName)).isRunning()) {
            stopWatch.stop();
        }
    }

    private static Map<String, Map<String, StopWatch>> getInstance() {
        Map<String, Map<String, StopWatch>> map;
        if (localWatchs.get() == null) {
            map = new ConcurrentHashMap<>();
            localWatchs.set(map);
        }
        return localWatchs.get();
    }

    private static Map<String, StopWatch> getByClass(String className) {
        LinkedHashMap<String, StopWatch> linkedHashMap;
        Map<String, Map<String, StopWatch>> map;
        if ((map = getInstance()).containsKey(className)) {
            return map.get(className);
        }
        linkedHashMap = new LinkedHashMap<>();
        map.put(className, linkedHashMap);
        return linkedHashMap;
    }

    private static StopWatch getByClassAndMethod(String className, String methodName) {
        StopWatch stopWatch;
        Map<String, StopWatch> map;
        if ((map = getByClass(className)).containsKey(methodName)) {
            return map.get(methodName);
        }
        stopWatch = new StopWatch(getId());
        map.put(methodName, stopWatch);
        return stopWatch;
    }

    public static void print(String className) {
        Iterator<Map.Entry<String, StopWatch>> iterator = getByClass(className).entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, StopWatch> next = iterator.next();
            print(className, next.getKey(), false);
        }
        getInstance().remove(className);
    }

    public static void print(String className, String methodName) {
        print(className, methodName, true);
    }

    private static void print(String className, String methodName, boolean bool) {
        StopWatch stopWatch = getByClassAndMethod(className, methodName);
        if (stopWatch.isRunning()) {
            stopWatch.stop();
        }
        if (log.isDebugEnabled()) {
            log.debug("StopWatchUtil: class={}, method={}", className, methodName);

            log.debug("{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
        }
        if (bool) {
            getByClass(className).remove(methodName);
        }
    }

    public static void setId(String id) {
        localId.set(id);
    }

    public static String getId() {

        return localId.get();
    }

    public static void clearId() {
        localId.remove();
    }

    public static void clearWatch() {
        localWatchs.remove();
    }
}
